package com.hexinfo.dmpro.admin.service;

import com.hexinfo.dmpro.common.base.BaseRequest;
import com.hexinfo.dmpro.common.base.BaseService;
import com.hexinfo.dmpro.admin.dao.MenuMapper;
import com.hexinfo.dmpro.admin.dao.RoleMapper;
import com.hexinfo.dmpro.common.exception.BadRequestException;
import com.hexinfo.dmpro.common.exception.EntityExistException;
import com.hexinfo.dmpro.admin.model.Menu;
import com.hexinfo.dmpro.admin.model.Role;
import com.hexinfo.dmpro.admin.model.vo.MenuMetaVo;
import com.hexinfo.dmpro.admin.model.vo.MenuVo;
import com.hexinfo.dmpro.common.utils.FileUtil;
import com.hexinfo.dmpro.common.utils.StringUtils;
import com.hexinfo.dmpro.common.utils.ValidationUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName MenuService
 * @Describe
 * @Author ml.wang
 * @Date 2021-06-16
 */
@Service
@CacheConfig(cacheNames = "menu")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class MenuService extends BaseService {

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private RoleMapper roleMapper;

    public List<Menu> queryAll(BaseRequest baseRequest) {
        return menuMapper.queryAll(baseRequest);
    }

    public List<Menu> findByRoles(List<Role> roles) {
        Set<Long> roleIds = roles.stream().map(Role::getId).collect(Collectors.toSet());
        return menuMapper.findByRoles(roleIds);
    }

    @Cacheable(key = "'pid:' + #p0")
    public List<Menu> findByPid(long pid) {
        return menuMapper.findByPid(pid);
    }

    @Cacheable(key = "'tree'")
    public Object getMenuTree(List<Menu> menus) {
        if (menus.size() == 0) {
            return Collections.emptyList();
        }
        List<Map<String,Object>> list = new LinkedList<>();
        menus.forEach(menu -> {
            if (menu!=null){
                List<Menu> menuList = findByPid(menu.getId());
                Map<String, Object> map = new HashMap<>(16);
                map.put("id", menu.getId());
                map.put("label", menu.getName());
                if(menuList != null && menuList.size() != 0){
                    map.put("children", getMenuTree(menuList));
                }
                list.add(map);
            }
        });
        return list;
    }

    public Menu findOne(Long id) {
        return menuMapper.findById(id);
    }

    public Set<Menu> getDeleteMenus(List<Menu> menuList, Set<Menu> menuSet) {
        // 递归找出待删除的菜单
        for (Menu menu : menuList) {
            menuSet.add(menu);
            List<Menu> menus = menuMapper.findByPid(menu.getId());
            if(menus != null && menus.size() != 0){
                getDeleteMenus(menus, menuSet);
            }
        }
        return menuSet;
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public void create(Menu menu) {
        if(menuMapper.isExistByName(menu.getName()) > 0){
            throw new EntityExistException(Menu.class, "name", menu.getName());
        }
        if(StringUtils.isNotBlank(menu.getComponentName())){
            if(menuMapper.isExistByComponentName(menu.getComponentName()) > 0){
                throw new EntityExistException(Menu.class, "componentName", menu.getComponentName());
            }
        }
        if(menu.getIFrame()){
            String http = "http://", https = "https://";
            if (!(menu.getPath().toLowerCase().startsWith(http) || menu.getPath().toLowerCase().startsWith(https))) {
                throw new BadRequestException("外链必须以http://或者https://开头");
            }
        }
        menu.setCreateTime(new Timestamp(System.currentTimeMillis()));
        menuMapper.save(menu);
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public void update(Menu menu) {
        if(menu.getId().equals(menu.getPid())) {
            throw new BadRequestException("上级不能为自己");
        }

        if(menu.getIFrame()){
            String http = "http://", https = "https://";
            if (!(menu.getPath().toLowerCase().startsWith(http) || menu.getPath().toLowerCase().startsWith(https))) {
                throw new BadRequestException("外链必须以http://或者https://开头");
            }
        }

        Menu existMenu = menuMapper.findById(menu.getId());
        ValidationUtil.isNull(existMenu.getId(),"Permission", "id", menu.getId());

        existMenu = menuMapper.findByName(menu.getName());

        if (existMenu != null && !existMenu.getId().equals(menu.getId())) {
            throw new EntityExistException(Menu.class, "name", menu.getName());
        }

        if(StringUtils.isNotBlank(menu.getComponentName())){
            existMenu = menuMapper.findByComponentName(menu.getComponentName());
            if(existMenu != null && !existMenu.getId().equals(menu.getId())){
                throw new EntityExistException(Menu.class, "componentName", menu.getComponentName());
            }
        }
        menu.setCreateTime(new Timestamp(System.currentTimeMillis()));
        menuMapper.modify(menu);
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public void delete(Set<Menu> menuSet) {
        roleMapper.untiedMenu(menuSet);
        menuMapper.delete(menuSet);
    }

    public void download(List<Menu> menus, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (Menu menu : menus) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("菜单名称", menu.getName());
            map.put("菜单类型", menu.getType() == 0 ? "目录" : menu.getType() == 1 ? "菜单" : "按钮");
            map.put("权限标识", menu.getPermission());
            map.put("外链菜单", menu.getIFrame() ? "是" : "否");
            map.put("菜单可见", menu.getHidden() ? "否" : "是");
            map.put("是否缓存", menu.getCache() ? "是" : "否");
            map.put("创建日期", menu.getCreateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    public List<MenuVo> buildMenus(List<Menu> menus) {
        if (menus.size() == 0) {
            return Collections.emptyList();
        }
        List<MenuVo> list = new LinkedList<>();
        menus.forEach(menu -> {
            List<Menu> menuList = menu.getChildren();
            MenuVo menuVo = new MenuVo();
            menuVo.setName(StringUtils.isNotEmpty(menu.getComponentName())  ? menu.getComponentName() : menu.getName());
            // 一级目录需要加斜杠，不然会报警告
            menuVo.setPath(menu.getPid() == 0 ? "/" + menu.getPath() : menu.getPath());
            menuVo.setHidden(menu.getHidden());
            // 如果不是外链
            if (!menu.getIFrame()) {
                if(menu.getPid() == 0){
                    menuVo.setComponent(StringUtils.isEmpty(menu.getComponent()) ? "Layout" : menu.getComponent());
                } else if (!StringUtils.isEmpty(menu.getComponent())) {
                    menuVo.setComponent(menu.getComponent());
                }
            }
            menuVo.setMeta(new MenuMetaVo(menu.getName(), menu.getIcon(), !menu.getCache()));
            if(menuList != null && menuList.size() != 0){
                menuVo.setAlwaysShow(true);
                menuVo.setRedirect("noredirect");
                menuVo.setChildren(buildMenus(menuList));
                // 处理是一级菜单并且没有子菜单的情况
            } else if(menu.getPid() == 0){
                MenuVo menuVo1 = new MenuVo();
                menuVo1.setMeta(menuVo.getMeta());
                // 非外链
                if(!menu.getIFrame()){
                    menuVo1.setPath("index");
                    menuVo1.setName(menuVo.getName());
                    menuVo1.setComponent(menuVo.getComponent());
                } else {
                    menuVo1.setPath(menu.getPath());
                }
                menuVo.setName(null);
                menuVo.setMeta(null);
                menuVo.setComponent("Layout");
                List<MenuVo> list1 = new ArrayList<>();
                list1.add(menuVo1);
                menuVo.setChildren(list1);
            }
            list.add(menuVo);
        });
        return list;
    }

    public Map<String, Object> buildTree(List<Menu> menus) {
        List<Menu> trees = new ArrayList<>();
        Set<Long> ids = new HashSet<>();
        for(Menu menu : menus) {
            if (menu.getPid() == 0) {
                trees.add(menu);
            }
            for (Menu it : menus) {
                if (it.getPid().equals(menu.getId())) {
                    if (menu.getChildren() == null) {
                        menu.setChildren(new ArrayList<>());
                    }
                    menu.getChildren().add(it);
                    ids.add(it.getId());
                }
            }
        }
        Map<String, Object> map = new HashMap<>(2);
        if(trees.size() == 0){
            trees = menus.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList());
        }
        map.put("content",trees);
        map.put("totalElements", menus.size());
        return map;
    }
}
